package puf.m2.automaton;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import puf.m2.util.ListUtil;

public class WordImpl implements Word<Letter> {
    /** Holds information that describe a word. i.e. a sequence of characters */
    public List<Letter> symbols = new ArrayList<Letter>();

    /** Convention for the empty string */
    public static String EMPTY = "\u03B5";

    /** Default constructor for creating a neutral word */
    public WordImpl() {}

    public WordImpl(Collection<Letter> string) {
        symbols.addAll(string);
    }

    public WordImpl(Letter[] string) {
        for (Letter symbol : string)
        symbols.add(symbol);
    }

    public boolean isNeutral() {
        return symbols == null;
    }

    /** Turn this tile into a neutral element */
    public void toNeutral() {
        if (symbols != null) {
            symbols.clear();
            symbols = null;
        }
    }

    /**
     * Return a inverse of the word
     * Example: invert(u1v1) = invert(v1).invert(u1)
     */
    public Word<Letter> invert() {
        // return the word itself if neutral because invert(1) = 1
        if (isNeutral()) return this;

        Collections.reverse(symbols);
        for (Letter ch : symbols) {
            ch.toggleBar();
        }
        return this;
    }

    /**
     * Concatenate the current word with another one
     * @param word
     *      the word that will be concatenated with
     * @return a new word
     */
    @SuppressWarnings("unchecked")
    public Word<Letter> concat(Word<Letter> word) {
        if (isNeutral()) return word;
        List<Letter> bigContent = ListUtil.concat(symbols, word.symbols());
        return new WordImpl(bigContent);
    }

    /**
     * Return a new word matching the least common suffix of 2 words (u V_s v)
     * e.g. u =  aba and
     *      v = aaba, then the new joint word is aaba.
     */
    public Word<Letter> jointSuffix(Word<Letter> word) {
        Word<Letter> newWord = null;

        // content1 is this.content, then content2 is:
        List<Letter> content2 = word.symbols();

        int contentSize1 = symbols.size(); // of the content of the word1
        int contentSize2 = content2.size(); // of the content of the word2

        int startIndexToSubList;
        List<Letter> subList;
        if (contentSize1 < contentSize2) {
            startIndexToSubList = contentSize2 - contentSize1;
            // we use subList to check the compatibility between content1 vs. content2
            subList = content2.subList(startIndexToSubList, contentSize2);
            // words are compatible
            if (subList.equals(symbols)) {
                newWord = new WordImpl(ListUtil.cloneList(content2));
            }
        } else { // contentSize1 > contentSize2
            startIndexToSubList = contentSize1 - contentSize2;
            subList = symbols.subList(startIndexToSubList, contentSize2);
            if (subList.equals(content2)) {
                newWord = new WordImpl(ListUtil.cloneList(symbols));
            }
        }

        // words are not compatible
        if (newWord == null) {
            System.out.println("[Word][jointSuffix] The words are not compatible to each other!");
        }
        return newWord;
    }

    /**
     * Return a new word matching the least common prefix of 2 words (u V_p v)
     * This is the reverse of jointSuffix
     * e.g. u = abba
     *      v = ab
     *      ->  abba
     */
    public Word<Letter> jointPrefix(Word<Letter> word) {
        Word<Letter> newWord = null;

        // content1 is this.content, then content2 is:
        List<Letter> content2 = word.symbols();

        int contentSize1 = symbols.size(); // of the content of the word1
        int contentSize2 = content2.size(); // of the content of the word2

        List<Letter> subList;
        if (contentSize1 < contentSize2) {
            // we use subList to check the compatibility between content1 vs. content2
            subList = content2.subList(0, contentSize1);
            // words are compatible
            if (subList.equals(symbols)) {
                newWord = new WordImpl(ListUtil.cloneList(content2));
            }
        } else { // contentSize1 > contentSize2
            subList = symbols.subList(0, contentSize2);
            if (subList.equals(content2)) {
                newWord = new WordImpl(ListUtil.cloneList(symbols));
            }
        }

        // words are not compatible
        if (newWord == null) {
            System.out.println("[Word][jointPrefix] The words are not compatible to each other!");
        }
        return newWord;
    }

    public String toString() {
        StringBuilder builder = new StringBuilder();
        for (Letter c : symbols) {
            builder.append(c.toString());
        }
        return builder.toString();
    }

    public List<Letter> symbols() {
        return symbols;
    }

    public void setSymbols(List<Letter> content) {
        this.symbols = content;
    }

    @Override
    public Letter symbolAt(int index) {
        return symbols.get(index);
    }

    @Override
    public int length() {
        return symbols.size();
    }
}
